package com.gaxy.gateway.redis;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * RedisConfig
 * 为什么需要RedisConfig？
 * 因为Redis自带的序列化器默认使用JDK的序列化工具 会显示乱码
 * 所以需要自己写一个序列化器 重写RedisTemplate
 *
 * @author Gaxy
 * @create 2024/06/08/下午 08:53
 */
@Configuration
public class RedisConfig {
    @Bean
    //RedisTemplate<String, Object>中，String -> key; Object -> value
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //StringRedisSerializer是Redis提供的一种序列化器，可以用作key的序列化器，因为key是String类型的
        RedisSerializer<String> redisSerializer = new StringRedisSerializer();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置key的序列化器
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        //value可能是对象的形式，因此value的序列化使用jackson库将对象序列化为json格式
        //设置key的序列化器
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
        return redisTemplate;
    }

    private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
        Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        // 配置ObjectMapper对象的属性可见性策略。ObjectMapper用于将Java对象与JSON数据相互转换
        ObjectMapper objectMapper = new ObjectMapper();

        //setVisibility方法用于配置属性的可见性
        //PropertyAccessor.ALL表示将对所有的属性类型（字段、getter、setter、creator等）进行配置
        //JsonAutoDetect.Visibility.ANY表示所有的属性都应该是可见的，不管它们是 private、protected、public 还是默认访问级别
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

        //使ObjectMapper在反序列化过程中遇到未知属性时不失败。
        //默认情况下，Jackson 在反序列化 JSON 数据时，如果遇到JSON中包含的属性在目标Java类中不存在，会抛出UnrecognizedPropertyException异常。
        //这段代码配置忽略这些未知属性，而不是抛出异常。
        //DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES表示Jackson是否应该在反序列化时因为未知属性失败,设置state为false
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        //用于启用Jackson的默认类型处理功能，以便在序列化和反序列化过程中保留类型信息
        //配置ObjectMapper在处理非最终类（NON_FINAL）时，通过一个类型属性（默认名为 @class）来保存和恢复对象的实际类型，保留了多态对象的实际类型信息
        //enableDefaultTyping用于启用默认的类型信息处理功能
        //ObjectMapper.DefaultTyping.NON_FINAL表示仅对非最终（non-final）类启用类型信息处理。最终类（final classes）和基本类型（primitive types）将不包括在内
        //JsonTypeInfo.As.PROPERTY 表示将类型信息包含为 JSON 属性。默认情况下，这个属性名为 @class。
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);


        jsonRedisSerializer.setObjectMapper(objectMapper);
        return jsonRedisSerializer;
    }

}
